RxSwift入坑(二)

RxSwift GitHub

RxCocoa APIS

  • NSObject

Observe

1
2
3
4
5
6
7
8
9
func showArticle() -> Observable<Bool> {
return articleModel.rx.observe(Int.self, "type").map { optionalType -> Bool in
guard let type = optionalType else {
return false
}
return type == 0 ? true : false
}
}
  • UIActivityIndicatorView
1
2
3
/// Bindable sink for `startAnimating()`, `stopAnimating()` methods.
public var isAnimating: UIBindingObserver<Base, Bool>
  • UIAlertAction
1
2
/// Bindable sink for `enabled` property.
public var isEnabled: UIBindingObserver<Base, Bool>
  • UIApplication
1
2
/// Bindable sink for `networkActivityIndicatorVisible`.
public var isNetworkActivityIndicatorVisible: UIBindingObserver<Base, Bool>
  • UIBarButtonItem
1
2
3
4
5
6
7
8
9
/// Bindable sink for `enabled` property.
public var isEnabled: UIBindingObserver<Base, Bool>
/// Bindable sink for `title` property.
public var title: UIBindingObserver<Base, String>
/// Reactive wrapper for target action pattern on `self`.
public var tap: ControlEvent<Void>
  • UIButton
1
2
/// Reactive wrapper for `TouchUpInside` control event.
public var tap: ControlEvent<Void>
  • UICollectionView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public func items<S: Sequence, O: ObservableType>
(_ source: O)
-> (_ cellFactory: @escaping (UICollectionView, Int, S.Iterator.Element) -> UICollectionViewCell)
-> Disposable where O.E == S
public func setDataSource(_ dataSource: UICollectionViewDataSource)
-> Disposable
public var itemSelected: ControlEvent<IndexPath>
public var itemDeselected: ControlEvent<IndexPath>
public func modelSelected<T>(_ modelType: T.Type) -> ControlEvent<T>
public func modelDeselected<T>(_ modelType: T.Type) -> ControlEvent<T>
public func model<T>(at indexPath: IndexPath) throws -> T
  • UIControl
1
2
3
4
5
6
7
8
9
10
/// Bindable sink for `enabled` property.
public var isEnabled: UIBindingObserver<Base, Bool>
/// Bindable sink for `selected` property.
public var isSelected: UIBindingObserver<Base, Bool>
/// Reactive wrapper for target action pattern.
///
/// - parameter controlEvents: Filter for observed event types.
public func controlEvent(_ controlEvents: UIControlEvents) -> ControlEvent<Void>
  • UIDatePicker
1
2
3
4
5
/// Reactive wrapper for `date` property.
public var date: ControlProperty<Date>
/// Reactive wrapper for `date` property.
public var value: ControlProperty<Date>
  • UIGestureRecognizer
1
2
/// Reactive wrapper for gesture recognizer events.
public var event: ControlEvent<Base>
  • UIImageView
1
2
/// Bindable sink for `image` property.
public var image: UIBindingObserver<Base, UIImage?>
  • UILabel
1
2
3
4
5
/// Bindable sink for `text` property.
public var text: UIBindingObserver<Base, String?>
/// Bindable sink for `attributedText` property.
public var attributedText: UIBindingObserver<Base, NSAttributedString?>
  • UINavigationController
1
2
3
4
5
/// Reactive wrapper for delegate method `navigationController(:willShow:animated:)`.
public var willShow: ControlEvent<ShowEvent>
/// Reactive wrapper for delegate method `navigationController(:didShow:animated:)`.
public var didShow: ControlEvent<ShowEvent>
  • UINavigationItem
1
2
/// Bindable sink for `title` property.
public var title: UIBindingObserver<Base, String?>

RxCocoa Highlighted API

  • bind
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
Creates new subscription and sends elements to observer.
In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables
writing more consistent binding code.
- parameter to: Observer that receives events.
- returns: Disposable object that can be used to unsubscribe the observer.
*/
public func bind<O: ObserverType>(to observer: O) -> Disposable where O.E == E {
return self.subscribe(observer)
}

example 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
example("PublishSubject"){
// PublishSubject
let subject = PublishSubject<String>()
let bindSubject = PublishSubject<String>()
subject.addObserver("2").disposed(by: disposeBag)
subject.bind(to: bindSubject).addDisposableTo(disposeBag)
bindSubject.subscribe(onNext: { (str) in
print("bind----\(str)")
}).addDisposableTo(disposeBag)
subject.onNext("🅱️")
}
--- PublishSubject example ---
Subscription: 2 Event: next(🅱️)
bind----🅱️

example 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cameraButton.rx.tap
.flatMapLatest { [weak self] _ in
return UIImagePickerController.rx.createWithParent(self) { picker in
picker.sourceType = .camera
picker.allowsEditing = false
}
.flatMap { $0.rx.didFinishPickingMediaWithInfo }
.take(1)
}
.map { info in
return info[UIImagePickerControllerOriginalImage] as? UIImage
}
.bind(to: imageView.rx.image)
.disposed(by: disposeBag)
  • UIBindingObserver

RxCocoa UI组件值绑定核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
Observer that enforces interface binding rules:
* can't bind errors (in debug builds binding of errors causes `fatalError` in release builds errors are being logged)
* ensures binding is performed on main thread
`UIBindingObserver` doesn't retain target interface and in case owned interface element is released, element isn't bound.
In case event binding is attempted from non main dispatch queue, event binding will be dispatched async to main dispatch
queue.
*/
public final class UIBindingObserver<UIElementType, Value> : ObserverType where UIElementType: AnyObject {
public typealias E = Value
weak var UIElement: UIElementType?
let binding: (UIElementType, Value) -> Void
/// Initializes `ViewBindingObserver` using
public init(UIElement: UIElementType, binding: @escaping (UIElementType, Value) -> Void) {
self.UIElement = UIElement
self.binding = binding
}
/// Binds next element to owner view as described in `binding`.
public func on(_ event: Event<Value>) {
if !DispatchQueue.isMain {
DispatchQueue.main.async {
self.on(event)
}
return
}
switch event {
case .next(let element):
if let view = self.UIElement {
binding(view, element)
}
case .error(let error):
bindingErrorToInterface(error)
case .completed:
break
}
}
/// Erases type of observer.
///
/// - returns: type erased observer.
public func asObserver() -> AnyObserver<Value> {
return AnyObserver(eventHandler: on)
}
}
1
2
3
4
5
6
7
8
UIControl+Rx
/// Bindable sink for `enabled` property.
public var isEnabled: UIBindingObserver<Base, Bool> {
return UIBindingObserver(UIElement: self.base) { (owner, value) in
owner.isEnabled = value
}
}

example

1
2
3
4
let control = UIControl(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
_ = Observable.just(true).bind(to: control.rx.isEnabled)
  • ControlEvent

RxCocoa UI组件点击事件绑定核心

类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/// Protocol that enables extension of `ControlEvent`.
public protocol ControlEventType : ObservableType {
/// - returns: `ControlEvent` interface
func asControlEvent() -> ControlEvent<E>
}
/**
Trait for `Observable`/`ObservableType` that represents event on UI element.
It's properties are:
- it never fails
- it won't send any initial value on subscription
- it will `Complete` sequence on control being deallocated
- it never errors out
- it delivers events on `MainScheduler.instance`
**The implementation of `ControlEvent` will ensure that sequence of events is being subscribed on main scheduler
(`subscribeOn(ConcurrentMainScheduler.instance)` behavior).**
**It is implementor's responsibility to make sure that that all other properties enumerated above are satisfied.**
**If they aren't, then using this trait communicates wrong properties and could potentially break someone's code.**
**In case `events` observable sequence that is being passed into initializer doesn't satisfy all enumerated
properties, please don't use this unit.**
*/
public struct ControlEvent<PropertyType> : ControlEventType {
public typealias E = PropertyType
let _events: Observable<PropertyType>
/// Initializes control event with a observable sequence that represents events.
///
/// - parameter events: Observable sequence that represents events.
/// - returns: Control event created with a observable sequence of events.
public init<Ev: ObservableType>(events: Ev) where Ev.E == E {
_events = events.subscribeOn(ConcurrentMainScheduler.instance)
}
/// Subscribes an observer to control events.
///
/// - parameter observer: Observer to subscribe to events.
/// - returns: Disposable object that can be used to unsubscribe the observer from receiving control events.
public func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
return _events.subscribe(observer)
}
/// - returns: `Observable` interface.
public func asObservable() -> Observable<E> {
return _events
}
/// - returns: `ControlEvent` interface.
public func asControlEvent() -> ControlEvent<E> {
return self
}
}

封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// Reactive wrapper for target action pattern.
///
/// - parameter controlEvents: Filter for observed event types.
public func controlEvent(_ controlEvents: UIControlEvents) -> ControlEvent<Void> {
let source: Observable<Void> = Observable.create { [weak control = self.base] observer in
MainScheduler.ensureExecutingOnScheduler()
guard let control = control else {
observer.on(.completed)
return Disposables.create()
}
let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) {
control in
observer.on(.next())
}
return Disposables.create(with: controlTarget.dispose)
}.takeUntil(deallocated)
return ControlEvent(events: source)
}

使用

1
2
3
4
5
6
7
extension Reactive where Base: UIButton {
/// Reactive wrapper for `TouchUpInside` control event.
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside)
}
}
  • Driver

用法同 bind(to )

专门针对于UI的特定可观察者类

特性

它不会发射出错误(Error)事件

对它的观察订阅是发生在主线程(UI线程)的

自带shareReplayLatestWhileConnected

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
Trait that represents observable sequence with following properties:
- it never fails
- it delivers events on `MainScheduler.instance`
- `shareReplayLatestWhileConnected()` behavior
- all observers share sequence computation resources
- it's stateful, upon subscription (calling subscribe) last element is immediatelly replayed if it was produced
- computation of elements is reference counted with respect to the number of observers
- if there are no subscribers, it will release sequence computation resources
`Driver<Element>` can be considered a builder pattern for observable sequences that drive the application.
If observable sequence has produced at least one element, after new subscription is made last produced element will be
immediately replayed on the same thread on which the subscription was made.
When using `drive*`, `subscribe*` and `bind*` family of methods, they should always be called from main thread.
If `drive*`, `subscribe*` and `bind*` are called from background thread, it is possible that initial replay
will happen on background thread, and subsequent events will arrive on main thread.
To find out more about traits and how to use them, please visit `Documentation/Traits.md`.
*/
public typealias Driver<E> = SharedSequence<DriverSharingStrategy, E>
1
2
3
4
5
6
asDriver(onErrorJustReturn onErrorJustReturn: Self.E)
asDriver(onErrorDriveWith onErrorDriveWith: RxCocoa.Driver<Self.E>)
asDriver(onErrorRecover onErrorRecover: (error: ErrorType) -> RxCocoa.Driver<Self.E>)
  • DisposeBag

类似于Objective-C ARC中的自动释放池机制。

方法addDisposableTo会对DisposeBag进行弱引用,所以这个DisposeBag要被实例引用着,一般可作为实例的成员变量,当实例被销毁了,成员DisposeBag会跟着销毁,从而使得RxSwift在此实例上绑定的资源得到释放。